#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/select.h>


#define SERV_PORT 8080

int main(int argc, char *argv[])
{
    int i, j, n, nready;

    int maxfd = 0;

    int listenfd, connfd;

    char buf[BUFSIZ]; /* #define INET_ADDRSTRLEN 16 */

    struct sockaddr_in clie_addr, serv_addr;
    socklen_t clie_addr_len;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family= AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port= htons(SERV_PORT);
    bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    listen(listenfd, 128);

    fd_set rset, allset; /* rset 读事件文件描述符集合 allset 用来暂存 */

    maxfd = listenfd;

    FD_ZERO(&allset);
    FD_SET(listenfd, &allset); /* 构造 select 监控文件描述符集 */

    while (1) 
    {
        rset = allset; /* 每次循环时都重新设置 select 监控信
        号集 */
        nready = select(maxfd+1, &rset, NULL, NULL, NULL);
        if (nready < 0)
            perror("select error");

        if (FD_ISSET(listenfd, &rset)) 
        { /* 说明有新的客户端链接请求 */

            clie_addr_len = sizeof(clie_addr);
            connfd = accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len); /* Acc
            ept 不会阻塞 */

            FD_SET(connfd, &allset); /* 向监控文件描述符集合 allset 添加新
            的文件描述符 connfd */

        if (maxfd < connfd)
            maxfd = connfd;// 保证始终存的是最大的

        if (0 == --nready) /* 只有 listenfd 有事件, 后续
        的 for 不需执行 */
            continue;

        }

        for (i = listenfd+1; i <= maxfd; i++) 
        { /* 检测哪个 clients 有数据就绪 */

            if (FD_ISSET(i, &rset)) 
            {

                if ((n = read(i, buf, sizeof(buf))) == 0) 
                { /* 当 client 关闭链接时,服务器端也关闭
                    对应链接 */
                    close(i);
                    FD_CLR(i, &allset); /* 解除 select 对此文件描述符的监
                    控 */

                } 
                else if (n > 0) 
                {

                    for (j = 0; j < n; j++)
                        buf[j] = toupper(buf[j]);
                        write(i, buf, n);
                }
            }
        }
    }

    close(listenfd);

    return 0;
}